package com.hontech.desktoppastacooker.ui.fragment

import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.hontech.bv.conn.serial.Uint16
import com.hontech.bv.conn.serial.Uint32
import com.hontech.desktoppastacooker.R
import com.hontech.desktoppastacooker.app.AppContext
import com.hontech.desktoppastacooker.conn.Proto
import com.hontech.desktoppastacooker.conn.SerialPortMod
import com.hontech.desktoppastacooker.conn.serial.ByteView
import com.hontech.desktoppastacooker.define.PORT_NAMES
import com.hontech.desktoppastacooker.device.Device
import com.hontech.desktoppastacooker.device.StatusEvent
import com.hontech.desktoppastacooker.ui.activity.BaseActivity
import com.hontech.desktoppastacooker.ui.window.showErr
import com.hontech.desktoppastacooker.ui.window.showProg
import com.hontech.desktoppastacooker.utils.*
import com.jaredrummler.materialspinner.MaterialSpinner
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import java.security.MessageDigest

class SettingFragment : Fragment() {

    private lateinit var versionDelegate: VersionDelegate

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val view = inflater.inflate(R.layout.fragment_setting, null)
        SettingDelegate(view.findViewById(R.id.id_setting_parts_serial_port))
        OtaDelegate(view)
        versionDelegate = VersionDelegate(view.findViewById(R.id.id_setting_parts_version))
        return view
    }

    override fun onStart() {
        super.onStart()
        eventBus.register(this)
    }

    override fun onStop() {
        super.onStop()
        eventBus.unregister(this)
    }

    @Subscribe(threadMode = ThreadMode.MAIN_ORDERED)
    fun onStatusEvent(_e: StatusEvent) {
        versionDelegate.update()
    }
}

private class VersionDelegate(view: View) {

    private val tvMcuVersion = view.findViewById<TextView>(R.id.id_setting_tv_mcu_version)
    private val tvClientVersion = view.findViewById<TextView>(R.id.id_setting_tv_client_version)

    init {
        tvClientVersion.text = "上位机版本:${Version}"
    }

    fun update() {
        val status = Device.status
        tvMcuVersion.text = "下位机版本:${status.versionString()}"
    }
}

private fun downloadForBrowser(id: Int) {
    val url = "${Http.BASE_URL}/dev_file/get?id=${id}"
    val uri = Uri.parse(url)
    val intent = Intent(Intent.ACTION_VIEW)
    intent.data = uri
    intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
    AppContext.startActivity(intent)
}


private suspend fun ota(data: ByteArray, cb: (Int) -> Unit) {

    SerialPortMod.request(
        3000,
        Proto.REQ_OTA_START,
        arrayOf(Uint32(data.size))
    ).assertErr()

    var id = 0
    var index = 0
    while (index < data.size) {
        var n = data.size - index
        if (n > 96) {
            n = 96
        }
        val bw = ByteView(data, index, n)
        SerialPortMod.request(
            3000,
            Proto.REQ_OTA_TRANSLATE,
            arrayOf(Uint16(id), bw)
        ).assertErr()
        index += n
        id ++
        cb( index * 100 / data.size )
    }

    val md5 = MessageDigest.getInstance("MD5").digest(data)

    SerialPortMod.request(
        3000,
        Proto.REQ_OTA_COMPLETE,
        arrayOf(ByteView(md5))
    ).assertErr()
}

private class FileItem(view: View) : RecyclerView.ViewHolder(view) {

    companion object {

        const val TYPE_MCU = 1
        const val TYPE_APK = 2

        fun sizeFormat(size: Int): String {
            if (size < 1024) {
                return "${size}Byte"
            }
            if (size < 1024 * 1024) {
                return "${size/1024}KB"
            }
            if (size < 1024 * 1024 * 1024) {
                return "${size/1024/1024}MB"
            }
            return "${size/1024/1024/1024}GB"
        }
    }

    private val tvTime = view.findViewById<TextView>(R.id.id_item_file_time)
    private val tvVersion = view.findViewById<TextView>(R.id.id_item_file_version)
    private val tvSize = view.findViewById<TextView>(R.id.id_item_file_size)
    private val tvDescription = view.findViewById<TextView>(R.id.id_item_file_description)
    private val btnUpdate = view.findViewById<Button>(R.id.id_item_file_btn_update)

    fun setUp(type: Int, file: Http.StoreFile) {
        tvTime.text = file.time()
        tvVersion.text = file.version
        tvSize.text = sizeFormat(file.size)
        tvDescription.text = file.description
        btnUpdate.onClick {

            if (type == TYPE_APK) {
                installApk(file.id)
            } else if (type == TYPE_MCU) {
                uploadMcu(file.id)
            }
        }
    }

    private suspend fun installApk(id: Int) {
        val dialog = showProg("更新上位机", "下载固件")

        try {
            writePermissionCheck()
            val path = Http.doDownloadApk(id) {
                dialog.setMsg("下载进度${it}%")
            }
            val ec = PromiseTask {
                installApk2(path)
            }.await(NetTask)

            if (!ec) {
                throw IllegalStateException("安装失败")
            }

        } catch (e: Exception) {
            e.printStackTrace()
            dialog.error(e)
        }
    }

    private suspend fun uploadMcu(id: Int) {

        val dialog = showProg("更新下位机", "下载固件")

        try {
            val data = Http.doDownloadBin(id)
            dialog.setMsg("下载成功开水更新")
            ota(data) {
                dialog.setMsg("进度:${it}%")
            }
            dialog.success("更新成功")
        } catch (e: Exception) {
            e.printStackTrace()
            dialog.error(e)
        }
    }
}

private class OtaDelegate(view: View) : RecyclerView.Adapter<FileItem>() {

    private val rv = view.findViewById<RecyclerView>(R.id.id_setting_rv)

    private val tmp = view.findViewById<View>(R.id.id_setting_parts_version)
    private val btnMCU = tmp.findViewById<Button>(R.id.id_setting_btn_get_mcu)
    private val btnApk = tmp.findViewById<Button>(R.id.id_setting_btn_get_apk)

    private var type = FileItem.TYPE_MCU
    private var files = emptyArray<Http.StoreFile>()

    init {
        rv.adapter = this
        rv.layoutManager = LinearLayoutManager(view.context, LinearLayoutManager.VERTICAL, false)

        btnMCU.onClick {
            val dialog = showProg("获取MCU固件", "")
            try {
                files = Http.queryMCUFile()
                type = FileItem.TYPE_MCU
                notifyDataSetChanged()
                dialog.success("获取成功")
            } catch (e: Exception) {
                e.printStackTrace()
                dialog.error(e)
            }
        }

        btnApk.onClick {
            val dialog = showProg("获取APK固件", "")
            try {
                files = Http.queryApkFile()
                type = FileItem.TYPE_APK
                notifyDataSetChanged()
                dialog.success("获取成功")
            } catch (e: Exception) {
                e.printStackTrace()
                dialog.error(e)
            }
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FileItem {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.item_file, parent, false)
        return FileItem(view)
    }

    override fun onBindViewHolder(holder: FileItem, position: Int) {
        holder.setUp(type, files[position])
    }

    override fun getItemCount(): Int {
        return files.size
    }
}

private class SettingDelegate(view: View) {

    private val sp = view.findViewById<MaterialSpinner>(R.id.id_setting_sp_ports)
    private val btnPortOpen = view.findViewById<Button>(R.id.id_setting_btn_port_open)
    private val btnExit = view.findViewById<Button>(R.id.id_setting_btn_exit)

    init {
        sp.setItems(PORT_NAMES)
        btnPortOpen.onClick(::onPortOpen)
        btnExit.onClick {
            try {
                SerialPortMod.close()
                BaseActivity.INSTANCE.finish()
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
    }

    private suspend fun onPortOpen() {

        try {
            if (SerialPortMod.isOpen()) {
                SerialPortMod.close()
                btnPortOpen.text = "打开"
                return
            }
            SerialPortMod.open( PORT_NAMES[sp.selectedIndex] )
            btnPortOpen.text = "关闭"
        } catch (e: Exception) {
            e.printStackTrace()
            showErr("打开串口失败", e)
        }
    }
}

